package logx

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	stdLog "log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/thinkeridea/go-extend/exbytes"
)

const (
	backupFileDelimiter = "-"
	// callerInnerDepth    = 5
	// flags = 0x0
)

var timeFormat = "2006-01-02 15:04:05.000000"

type MsgType int64

type (
	logEntry struct {
		Timestamp string `json:"@timestamp"`
		Level     string `json:"level"`
		Pid       int    `json:"pid"` //进程ID
		// Duration  string      `json:"duration,omitempty"`
		Content interface{} `json:"content"` //目前仅支持字符串格式
	}

	logOptions struct {
		serviceName string
		gzipEnabled bool
		keepDays    int
		flushtm     int
		path        string
		maxSize     int
		contentType int64 //0:普通 1:json类型
		timeFormat  string
	}
)

func NewLog(c *LogConf) *LoggingT {
	l := &LoggingT{
		toStderr: true,
	}
	if c == nil {
		c = NewConfig()
	}
	l.SetUp(c)
	return l
}

// severity identifies the sort of log: info, warning etc. It also implements
// the flag.Value interface. The -stderrthreshold flag is of type severity and
// should be modified only through the flag.Value interface. The values match
// the corresponding constants in C++.
type severity int32 // sync/atomic int32

// These constants identify the log levels in order of increasing severity.
// A message written to a high-severity log file is also written to each
// lower-severity log file.
const (
	infoLog severity = iota
	warningLog
	errorLog
	fatalLog
	numSeverity = 4
)

const severityChar = "IWEF"

var severityName = []string{
	infoLog:    "INFO",
	warningLog: "WARNING",
	errorLog:   "ERROR",
	fatalLog:   "FATAL",
}

func severityByName(s string) (severity, bool) {
	s = strings.ToUpper(s)
	for i, name := range severityName {
		if name == s {
			return severity(i), true
		}
	}
	return 0, false
}

// Level 表示输出的日志级别
type Level int32

type moduleSpec struct {
	filter []modulePat
}

// It holds a verbosity level and a file pattern to match.
type modulePat struct {
	pattern string
	literal bool // The pattern is a literal string
	level   Level
}

// match reports whether the file matches the pattern. It uses a string
// comparison if the pattern contains no metacharacters.
func (m *modulePat) match(file string) bool {
	if m.literal {
		return file == m.pattern
	}
	match, _ := filepath.Match(m.pattern, file)
	return match
}

var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")

// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
// that require filepath.Match to be called to match the pattern.
func isLiteral(pattern string) bool {
	return !strings.ContainsAny(pattern, `\*?[]`)
}

// traceLocation represents the setting of the -log_backtrace_at flag.
type traceLocation struct {
	file string
	line int
}

// isSet reports whether the trace location has been specified.
// logging.mu is held.
func (t *traceLocation) isSet() bool {
	return t.line > 0
}

// match reports whether the specified file and line matches the trace location.
// The argument file name is the full path, not the basename specified in the flag.
// logging.mu is held.
func (t *traceLocation) match(file string, line int) bool {
	if t.line != line {
		return false
	}
	if i := strings.LastIndex(file, "/"); i >= 0 {
		file = file[i+1:]
	}
	return t.file == file
}

var errTraceSyntax = errors.New("syntax error: expect file.go:234")

// flushSyncWriter is the interface satisfied by logging destinations.
type flushSyncWriter interface {
	Sync() error
	io.WriteCloser
}

// LoggingT collects all the global state of the logging setup.
type LoggingT struct {
	options logOptions

	toStderr bool
	toFile   bool

	stderrThreshold severity // The -stderrthreshold flag.

	freeList   *buffer
	freeListMu sync.Mutex

	// used to synchronize logging.
	mu sync.Mutex
	// file holds writer for each of the log types.
	file [numSeverity]flushSyncWriter
	// pcs is used in V to avoid an allocation when computing the caller's PC.
	pcs [1]uintptr
	// vmap is a cache of the V Level for each V() call site, identified by PC.
	// It is wiped whenever the vmodule flag changes state.
	vmap map[uintptr]Level
	// filterLength stores the length of the vmodule filter chain. If greater
	// than zero, it means vmodule is enabled. It may be read safely
	// using sync.LoadInt32, but is only modified under mu.
	filterLength int32

	traceLocation traceLocation
	// These flags are modified only under lock, although verbosity may be fetched
	// safely using atomic.LoadInt32.
	vmodule   moduleSpec // The state of the -vmodule flag.
	verbosity Level      // V logging level, the value of the -v flag/
}

func outputJson(info interface{}) (content []byte) {
	content, _ = json.Marshal(info)
	return
}

// buffer holds a byte Buffer for reuse. The zero value is ready for use.
type buffer struct {
	bytes.Buffer
	tmp   [64]byte // temporary byte array for creating headers.
	next  *buffer
	Entry logEntry
}

func (l *LoggingT) Close() {
	l.mu.Lock()
	l.flushAll()
	l.mu.Unlock()
}
func (l *LoggingT) DisableTerminal() {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.toStderr = false
}
func (l *LoggingT) EnableFile() {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.toFile = true
}

func (l *LoggingT) SetUp(c *LogConf) {
	l.mu.Lock()
	l.options.serviceName = c.ServiceName
	l.options.path = c.Path
	l.options.keepDays = c.KeepDays
	l.options.flushtm = c.FlushTm
	// if MsgType(c.ContentType) > MSG_SELF {
	if MsgType(c.ContentType) > MSG_JSON {
		c.ContentType = int64(MSG_DEFT)
	}

	atomic.StoreInt64(&l.options.contentType, c.ContentType)

	// l.options.contentType = MsgType(c.ContentType)
	l.options.gzipEnabled = c.Compress
	l.options.maxSize = c.MaxSize
	if len(c.TimeFormat) == 0 {
		l.options.timeFormat = timeFormat
	} else {
		l.options.timeFormat = c.TimeFormat
	}

	l.toStderr = c.WriteConsole
	l.toFile = c.WriteFile
	l.stderrThreshold = errorLog //但是V>=errorLog的日志还是会输出
	l.mu.Unlock()

	l.SetLevel(c.V)
	l.SetVModule(c.Vmodule)
	l.SetTrace(c.Backtrace)
}

func (l *LoggingT) SetLevel(level int) {
	v, err := strconv.Atoi(strconv.FormatInt(int64(level), 10))
	if err != nil {
		return
	}
	l.mu.Lock()
	defer l.mu.Unlock()
	l.setVState(Level(v), l.vmodule.filter, false)
}

// Syntax: -vmodule=recordio=2,file=1,gfs*=3
func (l *LoggingT) SetVModule(value string) error {
	var filter []modulePat
	for _, pat := range strings.Split(value, ",") {
		if len(pat) == 0 {
			// Empty strings such as from a trailing comma can be ignored.
			continue
		}

		patLev := strings.Split(pat, "=")
		if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
			return errVmoduleSyntax
		}
		pattern := patLev[0]
		v, err := strconv.Atoi(patLev[1])
		if err != nil {
			return errors.New("syntax error: expect comma-separated list of filename=N")
		}
		if v < 0 {
			return errors.New("negative value for vmodule level")
		}
		if v == 0 {
			continue // Ignore. It's harmless but no point in paying the overhead.
		}

		filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
	}
	l.mu.Lock()
	defer l.mu.Unlock()

	l.setVState(l.verbosity, filter, true)
	return nil
}

// Syntax: -log_backtrace_at=gopherflakes.go:234
// Note that unlike vmodule the file extension is included here.
func (l *LoggingT) SetTrace(backtrace string) error {
	l.mu.Lock()
	defer l.mu.Unlock()
	if backtrace == "" {
		l.traceLocation.line = 0
		l.traceLocation.file = ""
	}
	fields := strings.Split(backtrace, ":")
	if len(fields) != 2 {
		return errTraceSyntax
	}
	file, line := fields[0], fields[1]
	if !strings.Contains(file, ".") {
		return errTraceSyntax
	}
	v, err := strconv.Atoi(line)
	if err != nil {
		return errTraceSyntax
	}
	if v <= 0 {
		return errors.New("negative or zero value for level")
	}

	l.traceLocation.line = v
	l.traceLocation.file = file
	return nil
}
func (l *LoggingT) SetContentType(tp MsgType) error {
	if tp >= numMsgType {
		return nil
	}
	l.mu.Lock()
	defer l.mu.Unlock()
	// l.options.contentType = tp
	atomic.StoreInt64(&l.options.contentType, int64(tp))
	return nil
}

func (l *LoggingT) V(level Level) Verbose {
	// Here is a cheap but safe test to see if V logging is enabled globally.
	verbosity := Level(atomic.LoadInt32((*int32)(&l.verbosity)))
	if verbosity >= level {
		return Verbose{true, l}
	}

	// It's off globally but it vmodule may still be set.
	// Here is another cheap but safe test to see if vmodule is enabled.
	if atomic.LoadInt32(&l.filterLength) > 0 {
		// Now we need a proper lock to use the logging structure. The pcs field
		// is shared so we must lock before accessing it. This is fairly expensive,
		// but if V logging is enabled we're slow anyway.
		l.mu.Lock()
		defer l.mu.Unlock()
		if runtime.Callers(2, l.pcs[:]) == 0 {
			return Verbose{false, nil}
		}
		v, ok := l.vmap[l.pcs[0]]
		if !ok {
			v = l.setV(l.pcs[0])
		}
		out := Verbose{}

		out.ok = v >= level
		out.l = l
		return out
	}
	return Verbose{false, nil}
}

// Info logs to the INFO log.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func (l *LoggingT) Info(args ...interface{}) {
	l.print(infoLog, args...)
}

// InfoDepth acts as Info but uses depth to determine which call frame to log.
// InfoDepth(0, "msg") is the same as Info("msg").
func (l *LoggingT) InfoDepth(depth int, args ...interface{}) {
	l.printDepth(infoLog, depth, args...)
}

// Infoln logs to the INFO log.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func (l *LoggingT) Infoln(args ...interface{}) {
	l.println(infoLog, args...)
}

// Infof logs to the INFO log.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func (l *LoggingT) Infof(format string, args ...interface{}) {
	l.printf(infoLog, format, args...)
}

// Warning logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func (l *LoggingT) Warning(args ...interface{}) {
	l.print(warningLog, args...)
}

// WarningDepth acts as Warning but uses depth to determine which call frame to log.
// WarningDepth(0, "msg") is the same as Warning("msg").
func (l *LoggingT) WarningDepth(depth int, args ...interface{}) {
	l.printDepth(warningLog, depth, args...)
}

// Warningln logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func (l *LoggingT) Warningln(args ...interface{}) {
	l.println(warningLog, args...)
}

// Warningf logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func (l *LoggingT) Warningf(format string, args ...interface{}) {
	l.printf(warningLog, format, args...)
}

// Error logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func (l *LoggingT) Error(args ...interface{}) {
	l.print(errorLog, args...)
}

// ErrorDepth acts as Error but uses depth to determine which call frame to log.
// ErrorDepth(0, "msg") is the same as Error("msg").
func (l *LoggingT) ErrorDepth(depth int, args ...interface{}) {
	l.printDepth(errorLog, depth, args...)
}

// Errorln logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func (l *LoggingT) Errorln(args ...interface{}) {
	l.println(errorLog, args...)
}

// Errorf logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func (l *LoggingT) Errorf(format string, args ...interface{}) {
	l.printf(errorLog, format, args...)
}

// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func (l *LoggingT) Fatal(args ...interface{}) {
	l.print(fatalLog, args...)
}

// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
// FatalDepth(0, "msg") is the same as Fatal("msg").
func (l *LoggingT) FatalDepth(depth int, args ...interface{}) {
	l.printDepth(fatalLog, depth, args...)
}

// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func (l *LoggingT) Fatalln(args ...interface{}) {
	l.println(fatalLog, args...)
}

// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func (l *LoggingT) Fatalf(format string, args ...interface{}) {
	l.printf(fatalLog, format, args...)
}

// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
// It allows Exit and relatives to use the Fatal logs.
var fatalNoStacks uint32

// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func (l *LoggingT) Exit(args ...interface{}) {
	atomic.StoreUint32(&fatalNoStacks, 1)
	l.print(fatalLog, args...)
}

// ExitDepth acts as Exit but uses depth to determine which call frame to log.
// ExitDepth(0, "msg") is the same as Exit("msg").
func (l *LoggingT) ExitDepth(depth int, args ...interface{}) {
	atomic.StoreUint32(&fatalNoStacks, 1)
	l.printDepth(fatalLog, depth, args...)
}

// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
func (l *LoggingT) Exitln(args ...interface{}) {
	atomic.StoreUint32(&fatalNoStacks, 1)
	l.println(fatalLog, args...)
}

// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func (l *LoggingT) Exitf(format string, args ...interface{}) {
	atomic.StoreUint32(&fatalNoStacks, 1)
	l.printf(fatalLog, format, args...)
}

// setVState sets a consistent state for V logging.
// l.mu is held.
func (l *LoggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) {
	// Turn verbosity off so V will not fire while we are in transition.
	atomic.StoreInt32((*int32)(&l.verbosity), int32(0))
	// Ditto for filter length.
	atomic.StoreInt32(&l.filterLength, 0)

	// Set the new filters and wipe the pc->Level map if the filter has changed.
	if setFilter {
		l.vmodule.filter = filter
		l.vmap = make(map[uintptr]Level)
	}

	// Things are consistent now, so enable filtering and verbosity.
	// They are enabled in order opposite to that in V.
	atomic.StoreInt32(&l.filterLength, int32(len(filter)))
	atomic.StoreInt32((*int32)(&l.verbosity), int32(verbosity))
}

// getBuffer returns a new, ready-to-use buffer.
func (l *LoggingT) getBuffer() *buffer {
	l.freeListMu.Lock()
	b := l.freeList
	if b != nil {
		l.freeList = b.next
	}
	l.freeListMu.Unlock()
	if b == nil {
		b = new(buffer)
	} else {
		b.next = nil
		b.Reset()
	}
	return b
}

// putBuffer returns a buffer to the free list.
func (l *LoggingT) putBuffer(b *buffer) {
	// b.Entry.Level = ""
	// b.Entry.Timestamp = ""
	b.Entry.Content = nil
	if b.Len() >= 256 {
		// Let big buffers die a natural death.
		return
	}
	l.freeListMu.Lock()
	b.next = l.freeList
	l.freeList = b
	l.freeListMu.Unlock()
}

var timeNow = time.Now // Stubbed out for testing.

/*
header formats a log header as defined by the C++ implementation.
It returns a buffer containing the formatted header and the user's file and line number.
The depth specifies how many stack frames above lives the source line to be identified in the log message.

Log lines have this form:
	Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
where the fields are defined as follows:
	L                A single character, representing the log level (eg 'I' for INFO)
	mm               The month (zero padded; ie May is '05')
	dd               The day (zero padded)
	hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
	threadid         The space-padded thread ID as returned by GetTID()
	file             The file name
	line             The line number
	msg              The user-supplied message
*/
func (l *LoggingT) header(s severity, depth int) (*buffer, string, int) {
	_, file, line, ok := runtime.Caller(3 + depth)
	if !ok {
		file = "???"
		line = 1
	} else {
		slash := strings.LastIndex(file, "/")
		if slash >= 0 {
			file = file[slash+1:]
		}
	}
	return l.formatHeader(s, file, line), file, line
}

// formatHeader formats a log header using the provided file name and line number.
func (l *LoggingT) formatHeader(s severity, file string, line int) *buffer {
	if line < 0 {
		line = 0 // not a real line number, but acceptable to someDigits
	}
	if s > fatalLog {
		s = infoLog // for safety.
	}

	now := timeNow()
	buf := l.getBuffer()

	switch MsgType(atomic.LoadInt64(&l.options.contentType)) {
	case MSG_JSON:
		buf.Entry.Level = severityName[s]
		buf.Entry.Timestamp = now.Format(l.options.timeFormat)
		buf.Entry.Pid = pid
		buf.WriteString(file)
		buf.tmp[0] = ':'
		n := buf.someDigits(1, line)
		buf.tmp[n+1] = ' '
		buf.Write(buf.tmp[:n+2])
		return buf
	case MSG_SELF:
		return buf
	}
	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
	// It's worth about 3X. Fprintf is hard.
	_, month, day := now.Date()
	hour, minute, second := now.Clock()
	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
	buf.tmp[0] = severityChar[s]
	buf.twoDigits(1, int(month))
	buf.twoDigits(3, day)
	buf.tmp[5] = ' '
	buf.twoDigits(6, hour)
	buf.tmp[8] = ':'
	buf.twoDigits(9, minute)
	buf.tmp[11] = ':'
	buf.twoDigits(12, second)
	buf.tmp[14] = '.'
	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
	buf.tmp[21] = ' '
	buf.nDigits(7, 22, pid, ' ') // TODO: should be TID
	buf.tmp[29] = ' '
	buf.Write(buf.tmp[:30])
	buf.WriteString(file)
	buf.tmp[0] = ':'
	n := buf.someDigits(1, line)
	buf.tmp[n+1] = ']'
	buf.tmp[n+2] = ' '
	buf.Write(buf.tmp[:n+3])

	return buf
}

// Some custom tiny helper functions to print the log header efficiently.

const digits = "0123456789"

// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i].
func (buf *buffer) twoDigits(i, d int) {
	buf.tmp[i+1] = digits[d%10]
	d /= 10
	buf.tmp[i] = digits[d%10]
}

// nDigits formats an n-digit integer at buf.tmp[i],
// padding with pad on the left.
// It assumes d >= 0.
func (buf *buffer) nDigits(n, i, d int, pad byte) {
	j := n - 1
	for ; j >= 0 && d > 0; j-- {
		buf.tmp[i+j] = digits[d%10]
		d /= 10
	}
	for ; j >= 0; j-- {
		buf.tmp[i+j] = pad
	}
}

// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i].
func (buf *buffer) someDigits(i, d int) int {
	// Print into the top, then copy down. We know there's space for at least
	// a 10-digit number.
	j := len(buf.tmp)
	for {
		j--
		buf.tmp[j] = digits[d%10]
		d /= 10
		if d == 0 {
			break
		}
	}
	return copy(buf.tmp[i:], buf.tmp[j:])
}

func (l *LoggingT) println(s severity, args ...interface{}) {
	buf, file, line := l.header(s, 0)
	fmt.Fprintln(buf, args...)
	l.output(s, buf, file, line, false)
}

func (l *LoggingT) print(s severity, args ...interface{}) {
	l.printDepth(s, 1, args...)
}

func (l *LoggingT) printDepth(s severity, depth int, args ...interface{}) {
	buf, file, line := l.header(s, depth)
	fmt.Fprint(buf, args...)

	l.output(s, buf, file, line, false)
}

func (l *LoggingT) printf(s severity, format string, args ...interface{}) {
	buf, file, line := l.header(s, 0)
	fmt.Fprintf(buf, format, args...)

	l.output(s, buf, file, line, false)
}

func (l *LoggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) {
	buf := l.formatHeader(s, file, line)
	fmt.Fprint(buf, args...)

	l.output(s, buf, file, line, alsoToStderr)
}

// output writes the data to the log files and releases the buffer.
func (l *LoggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) {
	l.mu.Lock()
	if l.traceLocation.isSet() {
		if l.traceLocation.match(file, line) {
			buf.Write(stacks(false))
		}
	}
	data := buf.Bytes()
	contentType := MsgType(atomic.LoadInt64(&l.options.contentType))
	if contentType == MSG_JSON { //json格式
		buf.Entry.Content = exbytes.ToString(data)
		data, _ = json.Marshal(buf.Entry)
		if len(data) > 0 {
			data = append(data, '\n')
		}
	} else {
		if contentType < MSG_JSON {
			if data[len(data)-1] != '\n' {
				buf.WriteByte('\n')
				data = buf.Bytes()
			}
		}
	}

	stderrThreshold := severity(atomic.LoadInt32((*int32)(&l.stderrThreshold)))
	if alsoToStderr || l.toStderr || s >= stderrThreshold {
		os.Stderr.Write(data)
	}
	if l.toFile {
		if l.file[s] == nil {
			if err := l.createOutput(s); err != nil {
				os.Stderr.Write(data)
				l.mu.Unlock()
				// l.exit(err)
				return
			}
		}
		switch s {
		case fatalLog:
			l.file[fatalLog].Write(data)
			l.file[infoLog].Write(data)
		case errorLog:
			l.file[errorLog].Write(data)
			l.file[infoLog].Write(data)
		case warningLog:
			l.file[warningLog].Write(data)
			l.file[infoLog].Write(data)
		case infoLog:
			l.file[infoLog].Write(data)
		}
	}
	if s == fatalLog {
		// If we got here via Exit rather than Fatal, print no stacks.
		if atomic.LoadUint32(&fatalNoStacks) > 0 {
			l.mu.Unlock()
			timeoutFlush(10 * time.Second)
			os.Exit(1)
		}

		if !l.toStderr {
			os.Stderr.Write(stacks(false))
		}
		// Write the stack trace for all goroutines to the files.
		trace := stacks(true)
		logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
		for log := fatalLog; log >= infoLog; log-- {
			if f := l.file[log]; f != nil {
				f.Write(trace)
			}
		}
		l.mu.Unlock()
		timeoutFlush(10 * time.Second)
		os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
	}
	l.putBuffer(buf)
	l.mu.Unlock()
}

// timeoutFlush calls Flush and returns when it completes or after timeout
// elapses, whichever happens first.  This is needed because the hooks invoked
// by Flush may deadlock when glog.Fatal is called from a hook that holds
// a lock.
func timeoutFlush(timeout time.Duration) {
	done := make(chan bool, 1)
	go func() {
		Close() // calls logging.lockAndFlushAll()
		done <- true
	}()
	select {
	case <-done:
	case <-time.After(timeout):
		fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout)
	}
}

// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines.
func stacks(all bool) []byte {
	// We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
	n := 10000
	if all {
		n = 100000
	}
	var trace []byte
	for i := 0; i < 5; i++ {
		trace = make([]byte, n)
		nbytes := runtime.Stack(trace, all)
		if nbytes < len(trace) {
			return trace[:nbytes]
		}
		n *= 2
	}
	return trace
}

var logExitFunc func(error)

func (l *LoggingT) createOutput(sev severity) error {
	for s := sev; s >= infoLog && l.file[s] == nil; s-- {
		sevName := severityName[s]
		options := l.options
		sb, err := NewLogger(s, l, sevName, DefaultRotateRule(sevName, backupFileDelimiter, &options), options.gzipEnabled)
		if err != nil {
			return err
		}

		l.file[s] = sb
	}
	return nil
}

// flushAll flushes all the logs and attempts to "sync" their data to disk.
// l.mu is held.
func (l *LoggingT) flushAll() {
	for s := fatalLog; s >= infoLog; s-- {
		file := l.file[s]
		if file != nil {
			file.Close() // ignore error
		}
	}
}

// CopyStandardLogTo arranges for messages written to the Go "log" package's
// default logs to also appear in the Google logs for the named and lower
// severities.  Subsequent changes to the standard log's default output location
// or format may break this behavior.
//
// Valid names are "INFO", "WARNING", "ERROR", and "FATAL".  If the name is not
// recognized, CopyStandardLogTo panics.
func CopyStandardLogTo(name string) {
	sev, ok := severityByName(name)
	if !ok {
		panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name))
	}
	// Set a log format that captures the user's file and line:
	//   d.go:23: message
	stdLog.SetFlags(stdLog.Lshortfile)
	stdLog.SetOutput(logBridge(sev))
}

// setV computes and remembers the V level for a given PC
// when vmodule is enabled.
// File pattern matching takes the basename of the file, stripped
// of its .go suffix, and uses filepath.Match, which is a little more
// general than the *? matching used in C++.
// l.mu is held.
func (l *LoggingT) setV(pc uintptr) Level {
	fn := runtime.FuncForPC(pc)
	file, _ := fn.FileLine(pc)
	// The file is something like /a/b/c/d.go. We want just the d.
	if strings.HasSuffix(file, ".go") {
		file = file[:len(file)-3]
	}
	if slash := strings.LastIndex(file, "/"); slash >= 0 {
		file = file[slash+1:]
	}
	for _, filter := range l.vmodule.filter {
		if filter.match(file) {
			l.vmap[pc] = filter.level
			return filter.level
		}
	}
	l.vmap[pc] = 0
	return 0
}

// Verbose is a boolean type that implements Infof (like Printf) etc.
// See the documentation of V for more information.
type Verbose struct {
	ok bool
	l  *LoggingT
}

// Info is equivalent to the global Info function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Info(args ...interface{}) {
	if v.ok {
		v.l.print(infoLog, args...)
	}
}

// Infoln is equivalent to the global Infoln function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infoln(args ...interface{}) {
	if v.ok {
		v.l.println(infoLog, args...)
	}
}

// Infof is equivalent to the global Infof function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infof(format string, args ...interface{}) {
	if v.ok {
		v.l.printf(infoLog, format, args...)
	}
}
